#include "data_item.h"

#include "common/elements/element.h"
#include "common/elements/item.h"

namespace Database
{

DataItem::DataItem(Elements::Element* element)
{
    setElement(element);
}

DataItem::DataItem(std::shared_ptr<Elements::Element> element)
{
    m_element = element;
}

DataItem::DataItem(sqlite::database& database, unsigned id)
{
    fromDatabase(database, id);
}

DataElement* DataItem::clone() const
{
    if (m_element)
        return new DataItem(m_element->clone());

    return nullptr;
}

DataElement* DataItem::wrap(Elements::Element* element) const
{
    return new DataItem(element);
}

void DataItem::toDatabase(sqlite::database& database)
{
    const auto element = std::dynamic_pointer_cast<Elements::Item>(m_element);
    if (nullptr == element)
        return;

    if (0 == element->id())
    {
        const auto sqlStr = "INSERT INTO Item "
                            "( itemTypeId "
                            ", parentId "
                            ", stateId "
                            ", phaseId "
                            ", caption "
                            ", description "
                            ", searchCaption "
                            ", searchDescription "
                            ") VALUES (?,?,?,?,?,?,?,?);";
        database
            << sqlStr
            << element->itemTypeId()
            << element->parentId()
            << element->stateId()
            << element->phaseId()
            << element->caption()
            << element->description()
            << element->searchCaption()
            << element->searchDescription();
        element->setId(lastAddedId(database));
        return;
    }

    const auto sqlStr = "INSERT OR REPLACE INTO Item "
                        "( id "
                        ", itemTypeId "
                        ", parentId "
                        ", stateId "
                        ", phaseId "
                        ", caption "
                        ", description "
                        ", searchCaption "
                        ", searchDescription "
                        ") VALUES (?,?,?,?,?,?,?,?,?);";
    database
        << sqlStr
        << element->id()
        << element->itemTypeId()
        << element->parentId()
        << element->stateId()
        << element->phaseId()
        << element->caption()
        << element->description()
        << element->searchCaption()
        << element->searchDescription();
}

void DataItem::fromDatabase(sqlite::database& database, unsigned id)
{
    if (nullptr == m_element)
        m_element.reset(new Elements::Item());

    const auto element = std::dynamic_pointer_cast<Elements::Item>(m_element);

    const auto sqlStr = "SELECT "
                        "  id "
                        ", itemTypeId "
                        ", parentId "
                        ", stateId "
                        ", phaseId "
                        ", caption "
                        ", description "
                        ", searchCaption "
                        ", searchDescription "
                        "FROM Item WHERE id = ?;";
    database << sqlStr << id >>
        [element](
            unsigned id,
            unsigned itemTypeId,
            unsigned parentId,
            unsigned stateId,
            unsigned phaseId,
            std::string caption,
            std::string description,
            std::string searchCaption,
            std::string searchDescription
        )
    {
        element->setId(id);
        element->setItemTypeId(itemTypeId);
        element->setParentId(parentId);
        element->setStateId(stateId);
        element->setPhaseId(phaseId);
        element->setCaption(caption);
        element->setDescription(description);
        element->setSearchCaption(searchCaption);
        element->setSearchDescription(searchDescription);
    };
}

std::vector<Elements::Element*> DataItem::fromDatabaseElements(
    sqlite::database& database,
    std::vector<unsigned> ids
) const
{
    const auto sqlStr = "SELECT "
                        "  id "
                        ", itemTypeId "
                        ", parentId "
                        ", stateId "
                        ", phaseId "
                        ", caption "
                        ", description "
                        ", searchCaption "
                        ", searchDescription "
                        "FROM Item WHERE id in ("
        + questionMarks(ids.size())
        + ")";

    auto query = database << sqlStr;
    for (auto id : ids)
        query << id;

    std::vector<Elements::Element*> result;
    query >>
        [&result](
            unsigned id,
            unsigned itemTypeId,
            unsigned parentId,
            unsigned stateId,
            unsigned phaseId,
            std::string caption,
            std::string description,
            std::string searchCaption,
            std::string searchDescription
        )
    {
        auto element = new Elements::Item();
        element->setId(id);
        element->setItemTypeId(itemTypeId);
        element->setParentId(parentId);
        element->setStateId(stateId);
        element->setPhaseId(phaseId);
        element->setCaption(caption);
        element->setDescription(description);
        element->setSearchCaption(searchCaption);
        element->setSearchDescription(searchDescription);
        result.push_back(element);
    };
    return result;
}

// Получение идентификаторов ---------------------------------------------------

std::vector<unsigned> DataItem::ids(
    sqlite::database& database
)
{
    std::vector<unsigned> result;
    database << "SELECT id FROM Item;"
        >> [&result](unsigned id)
    {
        result.emplace_back(id);
    };
    return result;
}

std::vector<unsigned> DataItem::idsByItemTypeId(
    sqlite::database& database,
    unsigned id
)
{
    std::vector<unsigned> result;
    database << "SELECT id FROM Item WHERE itemTypeId = ?;" << id
        >> [&result](unsigned id)
    {
        result.emplace_back(id);
    };
    return result;
}

std::vector<unsigned> DataItem::idsByParentId(
    sqlite::database& database,
    unsigned id
)
{
    std::vector<unsigned> result;
    database << "SELECT id FROM Item WHERE parentId = ?;" << id
        >> [&result](unsigned id)
    {
        result.emplace_back(id);
    };
    return result;
}

std::vector<unsigned> DataItem::idsByStateId(
    sqlite::database& database,
    unsigned id
)
{
    std::vector<unsigned> result;
    database << "SELECT id FROM Item WHERE stateId = ?;" << id
        >> [&result](unsigned id)
    {
        result.emplace_back(id);
    };
    return result;
}

std::vector<unsigned> DataItem::idsByPhaseId(
    sqlite::database& database,
    unsigned id
)
{
    std::vector<unsigned> result;
    database << "SELECT id FROM Item WHERE phaseId = ?;" << id
        >> [&result](unsigned id)
    {
        result.emplace_back(id);
    };
    return result;
}

// Поиск по критериям ----------------------------------------------------------

std::vector<unsigned> DataItem::idsByText(
    sqlite::database& database,
    const std::string& value
)
{
    std::vector<unsigned> result;
    // TODO: реализовать метод:
    // * по caption
    // * по description
    // * по field value (не идентификаторы)
    // * по field value (логика через kind)
    // * по document caption
    // * по document description
    // * по comment content
    // * по comment document caption
    // * по comment document description
    return result;
}

std::vector<unsigned> DataItem::idsByCaption(
    sqlite::database& database,
    const std::string& value
)
{
    std::vector<unsigned> result;
    // TODO: реализовать метод
    return result;
}

std::vector<unsigned> DataItem::idsByDescription(
    sqlite::database& database,
    const std::string& value
)
{
    std::vector<unsigned> result;
    // TODO: реализовать метод
    return result;
}

std::vector<unsigned> DataItem::idsByFieldTypeValue(
    sqlite::database& database,
    unsigned fieldTypeId,
    const std::string& value
)
{
    std::vector<unsigned> result;
    // TODO: реализовать метод
    return result;
}

std::vector<unsigned> DataItem::idsByUserState(
    sqlite::database& database,
    const std::string& userValue,
    unsigned stateId
)
{
    std::vector<unsigned> result;
    // TODO: реализовать метод
    return result;
}

// Удаление --------------------------------------------------------------------

void DataItem::removeOne(
    sqlite::database& database,
    unsigned id
)
{
    std::vector<unsigned> ids = { id };
    remove(database, ids);
}

void DataItem::remove(
    sqlite::database& database,
    std::vector<unsigned> ids
)
{
    const auto sqlStr = "DELETE FROM Item WHERE id in ("
        + questionMarks(ids.size())
        + ")";

    auto query = database << sqlStr;
    for (auto id : ids)
        query << id;

    query.execute();
    query.reset();
}

} // namespace Elements
